package com.lifesting.tool.j2as3.visitor;

import java.util.HashMap;
import java.util.HashSet;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;

import com.lifesting.tool.j2as3.action.NodeWriter;
import com.lifesting.tool.j2as3.action.Util;
import com.lifesting.tool.j2as3.action.NodeWriter.Writer;
import com.lifesting.tool.j2as3.visitor.SubTypeVisitor.SubTypeDescriptor;

public class TransformVisitor extends ExpressionVisitor {
	
	protected int anonsCounter = 0;
	
	public TransformVisitor(NodeWriter nodeWriter) {
		super(nodeWriter);
	}
	
	@Override
	public void reset() {
		super.reset();
		anonsCounter = 0;
	}
	@Override
	public boolean visit(ClassInstanceCreation node) {
		Writer writer = o(node);
		Expression expr = node.getExpression();
		if (expr != null){
			logger.log("ClassInstanceCreation Expression: "+expr.toString());
			expr.accept(this);
			writer.print(".");
		}
		AnonymousClassDeclaration anons_class = node.getAnonymousClassDeclaration();
		String var = IdGenerator.getAnonVarName();
		writer.mark();
		rightRecursiveVisit(node.arguments(), writer);
		String arguments = writer.unmark();
		if (anons_class != null) {
			String anons_name = makeAnonsName();
			NodeMessage message = new NodeMessage(NodeMessage.SET_CONTAINER,anons_name);
			org.eclipse.jdt.core.dom.ASTNode parent_node = node.getParent();
			if (parent_node instanceof MethodInvocation)
			{
				writer.print(var);
				parent_node = findStatement(parent_node); 
				message.setSlot1(var);
				message.setSlot2(arguments);
			}else{
				writer.print("new "+anons_name + "("+arguments+")");
			}
			parent_node.setProperty(MESSAGE_KEY, message);
//			writer.print(anons_name);
			anons_class.setProperty(TYPE_KEY, node.getType());
			Util.setName(anons_class, anons_name);
			anons_class.accept(this);
		}else{
			writer.mark();
			node.getType().accept(this);
			String type_name =writer.unmark();
			NodeMessage message = new NodeMessage(NodeMessage.SET_CONTAINER,type_name);
			ASTNode parent_node = node.getParent();
			ITypeBinding tb = node.getType().resolveBinding();
			SubTypeDescriptor sd;
			if ((sd = subTypeDescriptors.get(tb.getQualifiedName()))!= null) {
				if (parent_node instanceof MethodInvocation) {
					writer.print(var);
					message.setSlot1(var);
					parent_node = findStatement(parent_node);
				} else {
					writer.print("new " + sd.realTypeName + "(" + arguments + ")");
				}
				parent_node.setProperty(MESSAGE_KEY, message);
			}else{
				writer.print("new ");
				writer.print(type_name);
				writer.print("(");
				writer.print(arguments);
				writer.print(")");
			}
		}
		return false;
	}
	private ASTNode findStatement(ASTNode parent_node) {
		ASTNode node = parent_node;
		while (node != null && !(node instanceof Statement)){
			node = node.getParent();
		}
		if (node == null){
			throw new RuntimeException("Unsupport node "+parent_node);
		}
		return node;
	}
	private String makeAnonsName() {
		anonsCounter++;
		return getUnitName()+"_"+anonsCounter;
	}

	@Override
	public void endVisit(ClassInstanceCreation node) {
		// TODO Auto-generated method stub
		super.endVisit(node);
	}



	@Override
	public boolean visit(SingleVariableDeclaration node) {
		Writer writer = o(node);
		visitChildren(node.modifiers());
		node.getName().accept(this);
		writer.print(":");
		if (node.getExtraDimensions() > 0){
			writer.print("Array");
		}else{
			node.getType().accept(this);
		}
		Expression init = node.getInitializer();
		if (init != null){
			writer.println("=");
			init.accept(this);
		}
		return false;
	}
	@Override
	public boolean visit(VariableDeclarationFragment node) {
		String name = node.getName().getFullyQualifiedName();
		Writer writer = o(node);
		writer.print(name);
		writer.print(":");
		if (node.getExtraDimensions() > 0) { // Array
			writer.print("Array");
		} else {
			// Special case, type is not the child of variable declaration
			// fragment, bust is field
			writer.print(getTypeName((Type) node.getParent().getProperty(
					TYPE_KEY)));
		}
		if (node.getInitializer() != null) {
			writer.print("=");
			node.getInitializer().accept(this);
		}
		NodeMessage nm = (NodeMessage) node.getProperty(MESSAGE_KEY);
		if (nm != null){
			nm.setMsg(name);
			node.getParent().setProperty(MESSAGE_KEY, nm);
		}
		return false;
	}
	@Override
	public boolean visit(CatchClause node) {
		Writer writer = o(node);
		writer.print("catch(");
		node.getException().accept(this);
		writer.println(")");
		node.getBody().setProperty(NO_BREAK, Boolean.TRUE);
		node.getBody().accept(this);
		
		return false;
	}
	
}
